package com.hanxu.mallproduct.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hanxu.mallproduct.service.CategoryBrandRelationService;
import com.hanxu.mallproduct.vo.Catelog2Vo;
import org.apache.ibatis.reflection.wrapper.ObjectWrapper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hanxu.common.utils.PageUtils;
import com.hanxu.common.utils.Query;

import com.hanxu.mallproduct.dao.CategoryDao;
import com.hanxu.mallproduct.entity.CategoryEntity;
import com.hanxu.mallproduct.service.CategoryService;
import org.springframework.util.StringUtils;


@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {
    @Autowired
    CategoryBrandRelationService categoryBrandRelationService;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    RedissonClient redissonClient;
    @Qualifier("redisTemplate")
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public List<CategoryEntity> listWithTree() {
        // 查所有
        List<CategoryEntity> entities = baseMapper.selectList(null);
        // 查分类
        List<CategoryEntity> categoryTree = new ArrayList<CategoryEntity>();
        for (CategoryEntity entity : entities) {
            if (entity.getParentCid() == 0) {
                categoryTree.add(entity);
            }
        }
        for (CategoryEntity categoryEntity : categoryTree) {
            buildTree(entities, categoryEntity);
        }
        categoryTree.sort(new Comparator<CategoryEntity>() {

            @Override
            public int compare(CategoryEntity o1, CategoryEntity o2) {
                if (o1.getSort() == null) {
                    o1.setSort(0);
                }
                if (o2.getSort() == 0) {
                    o2.setSort(0);
                }
                return o1.getSort() - o2.getSort();
            }
        });
        return categoryTree;
    }

    @Override
    public void removeMoveMenuByIds(List<Long> list) {
        //ToDo 检查当前删除的菜单，是否被别的地方引用
        baseMapper.deleteBatchIds(list);
    }

    @Override
    public Long[] findCatelogPath(Long catelogId) {
        List<Long> paths = new ArrayList<>();
        findParentPath(catelogId, paths);
        return (Long[]) paths.toArray(new Long[paths.size()]);
    }

    @Override
    public void updateCascade(CategoryEntity category) {
        this.updateById(category);
        categoryBrandRelationService.updateCategory(category.getCatId(),category.getName());
    }
    @Cacheable(value = "category",key = "#root.methodName")
    @Override
    public List<CategoryEntity> getLevel1Categories() {
        List<CategoryEntity> entities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
        return entities;
    }
    @Override
    public Map<String, List<Catelog2Vo>> getCatalogJson() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        /**
         * 空结果缓存：解决缓存穿透
         * 设置过期时间（加随机值）：解决缓存雪崩
         * 加锁：解决缓存击穿
         */
        // 占分布式锁，去redis占坑
        // 注意锁的名字，跟锁的粒度有关系
        System.out.println("配置："+redissonClient.getConfig());
        RLock lock = redissonClient.getLock("catalogJson-lock");
        System.out.println(lock.getName());
        lock.lock();
        try {
            // 加入缓存,获取json字符串
            String json = stringRedisTemplate.opsForValue().get("catalogJson");
            if(!StringUtils.hasText(json)){
                // 缓存中没有数据，查询数据库
                Map<String, List<Catelog2Vo>> catalogJsonFromDb = getCatalogJsonFromDb();
                // 放入缓存,将对象转为json放入缓存
                String s = objectMapper.writeValueAsString(catalogJsonFromDb);
                stringRedisTemplate.opsForValue().set("catalogJson", s);
                return catalogJsonFromDb;
            }else{
                return objectMapper.readValue(json, new TypeReference<Map<String,List<Catelog2Vo>>>(){});
            }
        } finally {
            lock.unlock();
        }
    }

    public Map<String, List<Catelog2Vo>> getCatalogJsonFromDb() {
        // 查出所有的一级分类
        List<CategoryEntity> level1Categories = getLevel1Categories();
        Map<String,List<Catelog2Vo>> parent_cid = new HashMap<>();
        for (CategoryEntity level1Category : level1Categories) {
            // 查出二级分类
            List<CategoryEntity> level2Categories = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", level1Category.getCatId()));
            List<Catelog2Vo> catelog2Vos = new ArrayList<>();
            for (CategoryEntity level2Category : level2Categories) {
                Catelog2Vo catelog2Vo = new Catelog2Vo();
                catelog2Vo.setCatalog1Id(level1Category.getCatId().toString());
                catelog2Vo.setId(level2Category.getCatId().toString());
                catelog2Vo.setName(level2Category.getName());
                // 查出三级分类
                List<CategoryEntity> level3Categories = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", level2Category.getCatId()));
                List<Catelog2Vo.Catelog3Vo> catelog3Vos = new ArrayList<>();
                for (CategoryEntity level3Category : level3Categories) {
                    Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo();
                    catelog3Vo.setId(level3Category.getCatId().toString());
                    catelog3Vo.setName(level3Category.getName());
                    catelog3Vo.setCatalog2Id(level2Category.getCatId().toString());
                    catelog3Vos.add(catelog3Vo);
                }
                catelog2Vo.setCatalog3List(catelog3Vos);
                catelog2Vos.add(catelog2Vo);
            }
            parent_cid.put(level1Category.getCatId().toString(), catelog2Vos);
        }

        return parent_cid;
    }

    private void findParentPath(Long catelogId, List<Long> paths) {
        CategoryEntity byId = this.getById(catelogId);
        paths.add(0, catelogId);
        if (byId.getParentCid() != 0) {
            findParentPath(byId.getParentCid(), paths);
        }
    }

    public void buildTree(List<CategoryEntity> entities, CategoryEntity root) {
        root.setChildren(new ArrayList<>());
        for (CategoryEntity entity : entities) {
            if (Objects.equals(entity.getParentCid(), root.getCatId())) {
                buildTree(entities, entity);
                root.getChildren().add(entity);
            }
        }
        root.getChildren().sort(new Comparator<CategoryEntity>() {

            @Override
            public int compare(CategoryEntity o1, CategoryEntity o2) {
                if (o1.getSort() == null) {
                    o1.setSort(0);
                }
                if (o2.getSort() == 0) {
                    o2.setSort(0);
                }
                return o1.getSort() - o2.getSort();
            }
        });
    }
}